#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
新浪彩票自动抓取脚本 - 定时任务版本
用于定时抓取最新开奖数据并存储
"""

import requests
import json
import time
import logging
import os
import sqlite3
from datetime import datetime
from typing import Dict, List, Optional
from crawler.database import db_manager
from enhanced_database import create_enhanced_manager

# 导入中奖查询和遗漏计算模块
from update_winning_status import WinningStatusUpdater
from calculate_omission_v2 import OmissionCalculatorV2
# 配置日志
logging.basicConfig(
    level=logging.DEBUG,  # 改为DEBUG级别以显示详细信息
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('logs/sina_auto_crawler.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class SinaAutoCrawler:
    """新浪彩票自动抓取器"""
    
    def __init__(self, db_path: str = "data/lottery_data.db"):
        self.base_url = "https://view.lottery.sina.com.cn/api/open/qgc"
        self.db_path = db_path
        # 创建增强版数据库管理器
        self.enhanced_db = create_enhanced_manager(db_manager)
        

        # lottery_types 字段需要更新
        self.lottery_types = {
            '101': 'ssq',
            '102': 'fc3d',
            '103': 'qlc',
            '104': 'kl8',
            '201': 'dlt',
            '202': 'pl3',
            '203': 'pl5',
            '204': 'qxc'
        }
        # 请求头
        self.headers = {
            'Accept': 'application/json, text/plain, */*',
            'Accept-Encoding': 'gzip, deflate, br, zstd',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Origin': 'https://lotto.sina.cn',
            'Referer': 'https://lotto.sina.cn/',
            'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1'
        }
        
        # 确保目录存在
        os.makedirs('logs', exist_ok=True)
        os.makedirs('data', exist_ok=True)

    
    
    def fetch_data(self) -> Optional[Dict]:
        """抓取开奖数据"""
        try:
            timestamp = int(time.time() * 1000)
            
            params = {
                'format': 'json',
                '__caller__': 'wap',
                '__version__': '1.0.0',
                '__verno__': '10000',
                't': timestamp
            }
            
            response = requests.get(
                self.base_url,
                params=params,
                headers=self.headers,
                timeout=30
            )
            
            response.raise_for_status()
            data = response.json()
            
            logger.info("数据抓取成功")
            return data
            
        except Exception as e:
            logger.error(f"数据抓取失败: {e}")
            return None

    def parse_and_save(self, data: Dict) -> List[str]:
        """解析并保存数据，返回新保存的期号列表"""
        saved_count = 0
        new_periods = []  # 记录新保存的期号

        try:
            if 'data' not in data['result']:
                logger.warning("响应数据中没有'data'字段")
                return new_periods
            crawl_time = datetime.now().isoformat()
            #双色球，七乐彩，大乐透，七星彩 有篮球，其他没有
            codes = ["101","103","201","204"]
            all_codes = ["101","102","103","104","201","202","203","204"]

            for lottery_code, lottery_info in data['result']['data'].items():
                if lottery_code not in all_codes:
                    continue
                if isinstance(lottery_info, dict):
                    lottery_name = self.lottery_types.get(lottery_code, f"未知彩种({lottery_code})")
                    numbers = []
                    try:
                        if lottery_code in codes:
                            # 处理有红球和蓝球的彩种
                            red_results = lottery_info.get('redResults', [])
                            blue_results = lottery_info.get('blueResults', [])

                            if isinstance(red_results, list):
                                numbers.append(" ".join(red_results))
                            else:
                                numbers.append(str(red_results) if red_results else "")

                            if isinstance(blue_results, list):
                                numbers.append(" ".join(blue_results))
                            else:
                                numbers.append(str(blue_results) if blue_results else "")
                        else:
                            # 处理只有开奖号码的彩种
                            open_results = lottery_info.get('openResults', [])
                            if isinstance(open_results, list):
                                numbers.append(" ".join(open_results))
                            else:
                                numbers.append(str(open_results) if open_results else "")
                    except Exception as e:
                        logger.error(f"处理号码数据时出错: {e}, 彩种: {lottery_code}")
                        numbers = ["", ""] if lottery_code in codes else [""]

                    # 安全处理日期转换
                    try:
                        create_time = lottery_info.get('createTime', '')
                        if create_time:
                            date_obj = datetime.strptime(create_time, "%Y-%m-%d %H:%M:%S")
                            draw_date = date_obj.strftime("%Y-%m-%d")
                        else:
                            # 如果没有createTime，使用当前日期
                            draw_date = datetime.now().strftime("%Y-%m-%d")
                    except ValueError as e:
                        logger.error(f"日期解析失败: {e}, createTime: {lottery_info.get('createTime')}")
                        draw_date = datetime.now().strftime("%Y-%m-%d")

                    # 插入或更新数据 - 使用MySQL的 ON DUPLICATE KEY UPDATE 语法
                    insert_sql = '''
                        INSERT INTO lottery_results
                        (lottery_type, period, all_numbers, draw_date, prize_info, updated_at, sales_amount, pool_amount)
                        VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
                        ON DUPLICATE KEY UPDATE
                        lottery_type = VALUES(lottery_type),
                        period = VALUES(period),
                        all_numbers = VALUES(all_numbers),
                        draw_date = VALUES(draw_date),
                        prize_info = VALUES(prize_info),
                        updated_at = NOW(),
                        sales_amount = VALUES(sales_amount),
                        pool_amount = VALUES(pool_amount)
                    '''

                    prizeList = []
                    prize_require={
                        "101":['中6+1','中6+0','中5+1','中5+0/4+1','中4+0/3+1','中2+1/1+1/0+1'],
                        "102":['定位中3码','2个数字相同且不定位中2码','不定位中3码'],
                        "103":['中7+0','中6+1','中6+0','中5+1','中5+0','中4+1','4+0'],
                        "104":[],
                        "201":['中5+2','中5+2','中5+1','中5+1','中5+0','中4+2','中4+1','中3+2','中4+0','中3+1/2+2','中3+0/2+1/1+2/0+2'],
                        "202":['定位中3码','2个数字相同且不定位中2码','不定位中3码'],
                        "203":['定位中5码'],
                        "204":['中6+1','中6+0','中5+1','中5+0/4+1','中4+0/3+1','中3+0/2+1/1+1/0+1']
                    }

                    prizeData = lottery_info.get('prizeList', '')

                    if prizeData != None and len(prizeData) > 0:
                        for i, prize in enumerate(prizeData):
                            # 安全获取require信息，避免索引越界
                            require_text = ''
                            if lottery_code != '104' and lottery_code in prize_require:
                                require_list = prize_require[lottery_code]
                                if i < len(require_list):
                                    require_text = require_list[i]
                                else:
                                    require_text = " "   # 默认值

                            newPrize = {
                                'num': prize.get('prizeCount', ''),
                                'require': require_text,
                                'prizename': prize.get('prizeDesc', ''),
                                'singlebonus': prize.get('prizeMoney', '')
                            }
                            prizeList.append(newPrize)

                    prizeList1 = json.dumps(prizeList, ensure_ascii=False)

                    params = (
                        self.lottery_types[lottery_code],
                        lottery_info.get('issueNo', ''),
                        json.dumps(numbers, ensure_ascii=False),  # 转换为JSON字符串
                        draw_date,
                        prizeList1,
                        datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                        lottery_info.get('saleMoney', ''),
                        lottery_info.get('poolMoney', ''),

                    )
                    try:

                        # 使用增强版数据库管理器执行SQL
                        result_info = self.enhanced_db.execute_insert_or_update(insert_sql, params)

                        if result_info['success']:
                            period = lottery_info.get('issueNo', '')
                            logger.info(f"✅ 保存 {lottery_name} 数据成功: {period} - {','.join(numbers)}")
                            logger.info(f"操作类型: {result_info['operation']}, {result_info['message']}")

                            # 如果是新增数据，记录期号用于后续计算
                            if result_info['operation'] in ['INSERT', 'insert']:
                                new_periods.append({
                                    'lottery_type': self.lottery_types[lottery_code],
                                    'period': period
                                })
                                logger.info(f"🆕 添加新期号到计算列表: {self.lottery_types[lottery_code]} - {period}")

                            saved_count += 1
                        else:
                            logger.error(f"❌ 保存 {lottery_name} 数据失败: {result_info['message']}")
                            logger.error(f"期次: {lottery_info.get('issueNo', '')}, 号码: {','.join(numbers)}")
                            continue

                    except Exception as db_error:
                        # logger.error(f"❌ 数据库操作失败: {db_error}")
                        # logger.error(f"错误类型: {type(db_error).__name__}")
                        # logger.error(f"彩种信息: 代码={lottery_code}, 名称={lottery_name}")
                        # logger.error(f"期次信息: {lottery_info.get('issueNo', '')}")
                        # logger.error(f"号码信息: {numbers}")
                        # logger.error(f"参数信息: {params}")

                        # 打印详细的错误堆栈
                        import traceback
                        logger.error(f"详细错误堆栈:\n{traceback.format_exc()}")

                        # 继续处理其他彩种，不中断整个流程
                        continue

        except Exception as e:
            logger.error(f"数据保存失败: {e}")

        return new_periods
    
    def log_crawl_result(self, status: str, message: str = "", data_count: int = 0):
        """记录抓取结果"""
        try:
            # 简单记录到日志文件
            log_message = f"抓取结果: 状态={status}, 消息={message}, 数据条数={data_count}"
            logger.info(log_message)
            
        except Exception as e:
            logger.error(f"记录抓取日志失败: {e}")
    
    def trigger_winning_calculation(self, new_periods):
        """触发中奖计算"""
        if not new_periods:
            logger.info("没有新开奖期号，跳过中奖计算")
            return

        logger.info("=" * 50)
        logger.info("开始执行中奖计算...")
        logger.info("=" * 50)

        # 按彩种分组
        lottery_groups = {}
        for period_info in new_periods:
            lottery_type = period_info['lottery_type']
            if lottery_type not in lottery_groups:
                lottery_groups[lottery_type] = []
            lottery_groups[lottery_type].append(period_info['period'])

        success_count = 0
        total_count = 0

        # 对每个彩种执行中奖计算
        for lottery_type, periods in lottery_groups.items():
            logger.info(f"处理彩种: {lottery_type}, 期号: {', '.join(periods)}")

            for period in periods:
                total_count += 1
                try:
                    # 重试机制：最多重试3次
                    max_retries = 3
                    retry_count = 0
                    success = False

                    while retry_count < max_retries and not success:
                        try:
                            updater = WinningStatusUpdater(
                                lottery_type=lottery_type,
                                issue_number=period
                            )
                            success = updater.run()
                            if success:
                                success_count += 1
                                logger.info(f"✅ 彩种 {lottery_type} 期号 {period} 中奖计算完成")
                            else:
                                logger.warning(f"⚠️ 彩种 {lottery_type} 期号 {period} 中奖计算无结果")
                            break
                        except Exception as retry_e:
                            retry_count += 1
                            if retry_count < max_retries:
                                logger.warning(f"🔄 彩种 {lottery_type} 期号 {period} 中奖计算失败，重试 {retry_count}/{max_retries}")
                                time.sleep(2 ** retry_count)  # 指数退避
                            else:
                                raise retry_e

                except Exception as e:
                    logger.error(f"❌ 中奖计算异常 {lottery_type} 期号 {period}: {e}")
                    import traceback
                    logger.debug(f"详细错误堆栈:\n{traceback.format_exc()}")

        logger.info("=" * 50)
        logger.info(f"中奖计算完成: {success_count}/{total_count} 成功")
        logger.info("=" * 50)

    def trigger_omission_calculation(self, new_periods):
        """触发遗漏值计算"""
        if not new_periods:
            logger.info("没有新开奖期号，跳过遗漏值计算")
            return

        logger.info("=" * 50)
        logger.info("开始执行遗漏值增量计算...")
        logger.info("=" * 50)

        # 按彩种分组
        lottery_groups = {}
        for period_info in new_periods:
            lottery_type = period_info['lottery_type']
            if lottery_type not in lottery_groups:
                lottery_groups[lottery_type] = []
            lottery_groups[lottery_type].append(period_info['period'])

        success_count = 0
        total_count = len(lottery_groups)

        # 对每个彩种执行遗漏值增量计算
        for lottery_type, periods in lottery_groups.items():
            logger.info(f"处理彩种: {lottery_type}, 期号: {', '.join(periods)}")

            calculator = None
            try:
                calculator = OmissionCalculatorV2()
                calculator.connect()

                # 使用增量计算方法
                calculator.calculate_by_periods(lottery_type, periods)
                success_count += 1
                logger.info(f"✅ 彩种 {lottery_type} 期号遗漏值计算完成")

            except Exception as e:
                logger.error(f"❌ 遗漏值计算异常 {lottery_type}: {e}")
                import traceback
                logger.debug(f"详细错误堆栈:\n{traceback.format_exc()}")
            finally:
                # 确保数据库连接被关闭
                if calculator:
                    try:
                        calculator.close()
                    except Exception as close_e:
                        logger.warning(f"⚠️ 关闭数据库连接失败: {close_e}")

        logger.info("=" * 50)
        logger.info(f"遗漏值计算完成: {success_count}/{total_count} 成功")
        logger.info("=" * 50)

    def run_once(self) -> bool:
        """执行一次抓取"""
        logger.info("开始执行数据抓取...")

        # 抓取数据
        data = self.fetch_data()

        if not data:
            self.log_crawl_result("failed", "数据抓取失败")
            return False

        # 保存数据
        new_periods = self.parse_and_save(data)

        # 添加调试信息
        logger.info(f"📊 parse_and_save返回结果: new_periods={new_periods}")
        logger.info(f"📊 new_periods长度: {len(new_periods)}")

        if new_periods:
            message = f"成功保存 {len(new_periods)} 条新记录"
            logger.info(message)
            self.log_crawl_result("success", message, len(new_periods))

            logger.info(new_periods)    
            # 🎯 自动触发后续计算
            logger.info("🚀 发现新开奖数据，自动触发后续计算...")

            # 1. 触发中奖计算
            self.trigger_winning_calculation(new_periods)

            # 2. 触发遗漏值增量计算
            self.trigger_omission_calculation(new_periods)

            return True
        else:
            message = "没有新数据保存"
            logger.warning(message)
            self.log_crawl_result("no_data", message, 0)
            return False
    
    def get_latest_results(self, limit: int = 10) -> List[Dict]:
        """获取最新的开奖结果"""
        try:
            # 使用数据库管理器查询
            sql = '''
                SELECT lottery_type, period, all_numbers, draw_date
                FROM lottery_results
                ORDER BY draw_date DESC, period DESC
                LIMIT %s
            '''
            
            results = []
            rows = db_manager.fetch_all(sql, (limit,))
            
            if rows:
                for row in rows:
                    results.append({
                        'lottery_type': row[0],
                        'period': row[1], 
                        'all_numbers': row[2],
                        'draw_date': row[3]
                    })
            
            return results
            
        except Exception as e:
            logger.error(f"查询最新结果失败: {e}")
            return []


def main():
    """主函数 - 用于命令行执行"""
    import sys
    
    crawler = SinaAutoCrawler()
    
    if len(sys.argv) > 1 and sys.argv[1] == "show":
        # 显示最新结果
        results = crawler.get_latest_results(20)
        
        if results:
            print("\n最新开奖结果:")
            print("-" * 80)
            for result in results:
                print(f"{result['lottery_type']} | {result['period']} | {result['all_numbers']} | {result['draw_date']}")
            print("-" * 80)
        else:
            print("暂无开奖数据")
    
    else:
        # 执行抓取
        success = crawler.run_once()
        if success:
            print("数据抓取完成")
        else:
            print("数据抓取失败")
            sys.exit(1)


if __name__ == "__main__":
    main()
